import json5 = require("json5");
import * as path from "path";

import * as prettier from "prettier";
import * as UnderscoreString from "underscore.string";

import * as fsp from "./helpers/fs-promises";
import * as log from "./helpers/log";

import * as CellValue from "./xlsx-cell-value";
import * as importerXlsx from "./importXlsx";

const isAlphanumeric = require("is-alphanumeric");

const xlsxDeclareFilename = "xlsx.d.ts";
let xlsxSheetTypeDeclareContent = "";
let xlsxDeclareContent = "";

const xlsxJsFilename = "xlsx.js";
let xlsxJsContent = "";
let xlsxGetterJsContent = "";

const prettierOptionsForTs: prettier.Options = {
	printWidth: 120,
	tabWidth: 4,
	useTabs: true,
	semi: true,
	trailingComma: "none",
	arrowParens: "always",
	parser: "typescript",
	proseWrap: "always"
};

const columnIsExportableInJs = (column: importerXlsx.Column) => {
	if (importerXlsx.ExportTarget.None == column.exportTarget) {
		return false;
	}

	if (importerXlsx.ExportTarget.ServerOnly == column.exportTarget) {
		return false;
	}

	return true;
};

const columnIsExportableInDeclare = (column: importerXlsx.Column, sheet: importerXlsx.Sheet) => {
	return columnIsExportableInJs(column);
};

const addSheet = (sheet: importerXlsx.Sheet) => {
	try {
		const sheetName = sheet.name;
		const className = UnderscoreString.classify(sheetName);
		const underscoredName = UnderscoreString.underscored(sheetName).toUpperCase();

		log.i(`客户端：${className} & ${underscoredName}`);

		let fileContent = "";
		let propertyCount = 0;
		let idIsString = false;

		// 生成定义

		fileContent += `// Generated from ${sheet.originalFilename}\n\n`;
		fileContent += `declare type ${className} = {`;

		for (const [columnNo, column] of sheet.columns.entries()) {
			if (0 === columnNo) {
				continue;
			}

			if (!columnIsExportableInDeclare(column, sheet)) {
				if (1 === columnNo) {
					log.w(`ID列不导出，本表无效。`);
					return;
				} else {
					continue;
				}
			}

			if (1 === columnNo) {
				if (CellValue.Type.String === column.type) {
					idIsString = true;
				}
			} else {
				// ID列不在属性列表中，但是计入属性数量，这样允许只有ID列的表

				if (column.description.length > 0) {
					fileContent += `\t/**\n`;
					fileContent += `\t * ${column.description.replace(/\n/g, "")}\n`;
					fileContent += `\t */\n`;
				}

				if (isAlphanumeric(column.name)) {
					fileContent += `readonly ${column.name}: ${CellValue.typeToTsTypeString(column.type)};\n\n`;
				} else {
					fileContent += `readonly ["${column.name}"]: ${CellValue.typeToTsTypeString(column.type)};\n\n`;
				}
			}

			propertyCount++;
		}

		fileContent += `};\n\n`;

		if (propertyCount <= 0) {
			log.w(`${sheetName} 没有任何可导出属性。`);
			return;
		}

		xlsxSheetTypeDeclareContent += fileContent;
		fileContent = undefined;

		// 生成根节点的属性声明

		xlsxDeclareContent += `\n\t/**\n`;
		xlsxDeclareContent += `\t * Generated from ${sheet.originalFilename}\n`;
		xlsxDeclareContent += `\t */\n`;

		if (idIsString) {
			xlsxDeclareContent += `\treadonly ${underscoredName}: { readonly [id: string]: ${className}; };\n`;
		} else {
			xlsxDeclareContent += `\treadonly ${underscoredName}: { readonly [id: number]: ${className}; };\n`;
		}

		// 生成JS数据

		xlsxJsContent += `\n\t/**\n`;
		xlsxJsContent += `\t * Generated from ${sheet.originalFilename}\n`;
		xlsxJsContent += `\t */\n`;
		xlsxJsContent += `\t${underscoredName}: `;
		xlsxJsContent += "{\n";

		for (const rowData of sheet.data.values()) {
			const obj: any = {};

			for (const [columnNo, cellData] of rowData.entries()) {
				if (0 === columnNo) {
					continue;
				}

				if (1 === columnNo) {
					xlsxJsContent += `[${JSON.stringify(cellData)}]: `;
					continue;
				}

				const column = sheet.columns[columnNo];

				if (!columnIsExportableInJs(column)) {
					continue;
				}

				obj[column.name] = cellData;
			}

			xlsxJsContent += `${json5.stringify(obj)},\n`;
		}

		xlsxJsContent += "},\n";

		xlsxGetterJsContent += `\n\nfor (var key in window.XLSX.${underscoredName}) {\n`;
		xlsxGetterJsContent += `Object.defineProperties(window.XLSX.${underscoredName}[key], {\n`;

		xlsxGetterJsContent += `});}`;
	} catch (err) {
		log.e(err);
	}
};

export const generate = async (clientCfgDir: string, clientLocalesDir?: string) => {
	try {
		const sheets = importerXlsx.sheets;

		// 保证输出目录存在

		await Promise.all([fsp.mkdirp(clientCfgDir)]);

		// 移除旧文件

		await Promise.all([
			fsp.del(path.join(clientCfgDir, xlsxDeclareFilename)),
			fsp.del(path.join(clientCfgDir, xlsxJsFilename))
		]);

		// 配置表初始化

		xlsxDeclareContent = `declare const XLSX: {\ngenerationDate: Date,\n`;
		xlsxJsContent = `window.XLSX = {\ngenerationDate: new Date(${Date.now()}),\n`;

		// 配置表逐个加入

		const sheetNames: string[] = [];

		for (const sheetName of sheets.keys()) {
			sheetNames.push(sheetName);
		}

		sheetNames.sort();
		sheetNames.forEach(sheetName => {
			addSheet(sheets.get(sheetName));
		});

		// 配置表解析结束

		let comment = `\n\t/**\n`;
		comment += `\t * 将参数以“-”连接作为ID在指定sheet中找到匹配的行\n`;
		comment += `\t * @param sheet XLSX中的表\n`;
		comment += `\t * @param args 将被“-”连接的参数\n`;
		comment += `\t * @returns 匹配行的数据\n`;
		comment += `\t */\n`;

		xlsxDeclareContent += comment;
		xlsxDeclareContent += `readonly findOne: (sheet: any, ...args: any[]) => any;\n`;
		xlsxDeclareContent += `};`;

		xlsxJsContent += comment;
		xlsxJsContent += `findOne: function (sheet) {
		var args = [];
		for (var _i = 1; _i < arguments.length; _i++) {
			args[_i - 1] = arguments[_i];
		}
		var key = args.join("-");
		return sheet[key];
		}\n`;
		xlsxJsContent += `};`;

		// 写文件，需要避免同时写入导致Prettier内存占用过高

		await fsp.writeFile(
			path.join(clientCfgDir, xlsxDeclareFilename),
			prettier.format(xlsxSheetTypeDeclareContent + xlsxDeclareContent, prettierOptionsForTs)
		);
		await fsp.writeFile(
			path.join(clientCfgDir, xlsxJsFilename),
			prettier.format(xlsxJsContent + xlsxGetterJsContent, prettierOptionsForTs)
		);
	} catch (err) {
		log.e(err);
	}
};
